<?php

class TweetController extends Controller {
    
    /**
     * Post a Tweet
     * @param string $content
     * @param string $image
     * return json status
     */
    public function actionPost() {
        // Get param
        $content = Yii::app()->request->getParam('content');
        $image   = Yii::app()->request->getParam('image');
        
        // Save file
        $imageUrl = $this->saveImage($image);
        $content .= " ".$imageUrl;
        // Save to model
        $tweet = new Tweets();
        $tweet->content = $content;
        $tweet->user_id = Yii::app()->user->id;
        $tweet->created_time = new CDbExpression('NOW()');
        
        // initiate result
        $result = array('result'=>1);
        // validate and save content;
        if (!$this->savePost($tweet)){
            $result['result'] = 0;
            $result['errors'] = $tweet->errors;
            $tweet->parent_id = $tweet->id;
            $tweet->save();
        }
        
        $this->end($result);
    }
    
    private function saveImage($image) {
        if (!$image) return '';
        $binary   = base64_decode($image);
        $filePath = Yii::getPathOfAlias('webroot').'/uploads/';
        $fileName = date("Ymdhi").  Yii::app()->user->id. ".png";
        
        $file   = fopen($filePath.$fileName, "wb+");
        fwrite($file, $binary);
        fclose($file);
        return $fileName;
    }
    
    /**
     * 
     * @param Tweets $tweet a tweet which want to save
     * @return boolean
     */
    private function savePost($tweet){
        if (!$tweet->save()) return false;
        
        // After save to tweet table : save to mentions and timeline
        // 1. Mentions
        $this->saveMentions($tweet);
        
        // 2. Timeline
        //
        $this->saveTimeline($tweet);
        
        return true;
    }
    
    
    /**
     * Reply a Tweet
     * @param string $content
     * @param string $image
     * return json status
     */
    public function actionReply() {
        // Get param
        $parent_id = Yii::app()->request->getParam('tweet_id');
        $content   = Yii::app()->request->getParam('content');
        $image     = Yii::app()->request->getParam('image');
        
        // Save file
        $imageUrl = $this->saveImage($image);
        $content .= " ".$imageUrl;
        
        // Save to model
        $tweet = new Tweets();
        $tweet->content   = $content;
        $tweet->user_id   = Yii::app()->user->id;
        $tweet->parent_id = $parent_id;
        $tweet->created_time = new CDbExpression('NOW()');
        
        // initiate result
        $result = array('result'=>1);
        // validate and save content;
        if (!$this->savePost($tweet)){
            $result['result'] = 0;
            $result['errors'] = $tweet->errors;
        }
        
        $this->end($result);
    }
    
    /**
     * Save mention list in tweet
     * @param Tweets $tweet a tweet which want to save
     */
    private function saveMentions($tweet) {
        $gcmIDs = array();
        preg_match_all("/@\w+\b/", $tweet->content, $matchs);
        foreach ($matchs[0] as $mentionItem){
            $mentionItem = substr($mentionItem, 1);
            $mention = new Mentions();
            $mention->tweet_id = $tweet->id;
            $user = Users::model()->findByAttributes(array("username"=>$mentionItem));
            if ($user) {
                $mention->user_id  = $user->id;
                if ($mention->save()){
                    $gcms = Gcm::model()->findAllByAttributes(array('user_id'=>$user->id));
                    foreach ($gcms as $gcm)
                        $gcmIDs[] = $gcm->gcm_id;
                }
            }
        }
        GcmCore::sendGCM($gcmIDs, "New mentions");
    }
    
    /**
     * Save to follower timeline
     * @param Tweets $tweet a tweet which want to save
     */
    private function saveTimeline($tweet) {
        $this->way5($tweet);
    }
    
    private function way1($tweet) {
        $user = Users::model()->findByPk(Yii::app()->user->id);
        $follows = Follows::model()->findAllByAttributes(array("follow_id"=>$user->id));
        //Yii::app()->db->createCommand("LOCK TABLE timeline WRITE;")->execute();
        $sql     = "INSERT INTO timeline values (null, $user->id, $tweet->id);";
        foreach ($follows as $follow) {
            $sql .= "INSERT INTO timeline values (null, $follow->user_id, $tweet->id);";
        }
        $sql .= "UNLOCK TABLE;";
        $command = Yii::app()->db->createCommand($sql);
        $command->execute();
    }
    
    private function way2($tweet) {
        $t = microtime(true);
        $user = Users::model()->findByPk(Yii::app()->user->id);
        $follows = Follows::model()->findAllByAttributes(array("follow_id"=>$user->id));
        $sql     = "null\t".$user->id."\t".$tweet->id."";
        foreach ($follows as $follow) {
            $sql .= "\nnull\t$follow->user_id\t$tweet->id";
        }
        
        $fileName ="/Volumes/Rdisk/insert.sql";
        $file   = fopen($fileName, "w+");
        fwrite($file, $sql);
        fclose($file);
        
        //$sql = "INSERT INTO timeline select null,user_id, $tweet->id from follows where follow_id = $user->id";
        $command = Yii::app()->db->createCommand("LOAD DATA INFILE '$fileName' INTO TABLE timeline");
        $command->execute();
        die(microtime(true)-$t);
    }
    
    private function way3($tweet) {
    	//$t = microtime(true);
    	$file = "/Volumes/Rdisk/".Yii::app()->user->id."_".time();
    	$user_id = Yii::app()->user->id;
    	$command = Yii::app()->db->createCommand("SELECT user_id, '$tweet->id' INTO OUTFILE '$file' FROM follows WHERE follow_id = '$user_id'");
    	$command->execute();
    	
    	$sql = "INSERT INTO timeline select null,user_id, $tweet->id from follows where follow_id = $user->id";
    	$command = Yii::app()->db->createCommand("LOAD DATA INFILE '$file' INTO TABLE timeline;");
    	$command->execute();
        unlink($file);
    	//echo(microtime(true)-$t);
    }
    
    private function way4($tweet) {
        $t = microtime(true);
        $file = "/Volumes/RAMDISK/".Yii::app()->user->id."_".time();
        $user_id = Yii::app()->user->id;
        $command = Yii::app()->db->createCommand("SELECT null, user_id, '$tweet->id' INTO OUTFILE '$file' FROM follows WHERE follow_id = '$user_id'");
        $command->execute();
        
        //$sql = "INSERT INTO timeline select null,user_id, $tweet->id from follows where follow_id = $user->id";
        
        exec("/Applications/MAMP/Library/bin/mysql -u root -proot -h localhost twitter -e 'load data infile \"$file\" into table timeline' &> /dev/null &");
        //unlink($file);
        echo(microtime(true)-$t);  
    }
    
    private function way5($tweet) {
        $t = microtime(true);
        $user_id = Yii::app()->user->id;
        $db = Yii::app()->db->connectionString;
        $dbName = explode("dbname=", $db);
        $dbName = $dbName[1];
        $sql = "INSERT INTO timeline values (null, $user_id, $tweet->id);";
        $sql .= "INSERT INTO timeline SELECT null, user_id, '$tweet->id' from follows WHERE follow_id = '$user_id'";
        exec("/Applications/MAMP/Library/bin/mysql -u root -proot -h localhost $dbName -e '$sql' &> /dev/null &");
        logged(microtime(true)-$t);  
    }
            
    
    /**
     * Destroy a posted tweet
     * @param int $tweet_id ID of which tweet desired to destroy
     */
    public function actionDestroy(){
        // Get param
        $tweet_id = Yii::app()->request->getParam("tweet_id");
        $tweet    = Tweets::model()->findByPk($tweet_id);
        
        if ($tweet){
            // initiate result
            $result = array('result'=>1);
            if (!$tweet || !$tweet->delete()){
                $result['result'] = 0;
                $result['errors'] = $tweet->errors;
            }

            $this->end($result);
        }
        
        $this->end(array(
            'result'=>'0',
            'errors'=>array('Not exists'))
        );    
    }
    
    /**
     * Favorite a tweet
     * @param int $tweet_id ID of a tweet
     */
    public function actionFavorite(){
        // Get param
        $tweet_id        = Yii::app()->request->getParam("tweet_id");
        $tweet_real_id   = Tweets::findRealTweetID($tweet_id);
        
        $user_id  = Yii::app()->user->id;
        $favorite = Favorites::model()->findByAttributes(array(
           "user_id"  =>$user_id,
           "tweet_id" =>$tweet_real_id,
        ),array('select'=>'id'));
        
        // initiate result
        $result = array('result'=>1);
        
        // If this tweet hasn't been favorited
        if (!$favorite){
            $favorite = new Favorites();
            $favorite->user_id  = $user_id;
            $favorite->tweet_id = $tweet_real_id;
            
            if (!$favorite->save()){
                $result['result'] = 0;
                $result['errors'] = $favorite->errors;
            }
        }
        
        $this->end($result);
    }
    
    
    /**
     * Unfavorite a tweet
     * @param int $tweet_id ID of a tweet
     */
    public function actionUnfavorite(){
        // initiate result
        $result = array('result'=>1);
        
        // Get param
        $tweet_id        = Yii::app()->request->getParam("tweet_id");
        $user_id         = Yii::app()->user->id;
        $tweet_real_id   = Tweets::findRealTweetID($tweet_id);
        
        // Get model
        $favorite = Favorites::model()->findByAttributes(array(
            'user_id'  =>$user_id,
            'tweet_id' =>$tweet_real_id,
        ),array('select'=>'id'));
        
        if ($favorite && !$favorite->delete()){
            $result['result'] = 0;
            $result['errors'] = $favorite->errors;
        }
        
        $this->end($result);
    }
    
    /**
     * Retweet a tweet
     * @param int $tweet_id ID of a tweet
     */
    public function actionRetweet(){
        // Get param
        $tweet_id = Yii::app()->request->getParam("tweet_id");
        $tweet = Tweets::findRealTweetID($tweet_id, true);
        $tweet_id = $tweet->id;
        
        $user_id  = Yii::app()->user->id;
        if ($user_id == $tweet->user_id)
            die (CJSON::encode(array(
               'result' => 0,
               'errors' => array(
                   'user'=>"Can't retweet your tweet",
               ),
            )));
        
        // Find Model
        $retweet  = Tweets::model()->findByAttributes(array(
            'user_id'   => $user_id,
            'parent_id' => $tweet_id,
            'content'   => NULL,
        ),array('select'=>'id'));
        
        // If haven't retweeted before, create a new one
        $result = array("result"=>1);
        if (!$retweet){
            $retweet = new Tweets();
            $retweet->user_id      = $user_id;
            $retweet->parent_id    = $tweet_id;
            $retweet->created_time = new CDbExpression('NOW()');
            if (!$retweet->save()){
                $result['result'] = 0;
                $result['errors'] = $retweet->errors;
            }
            else {
                // Post this retweet to follower timeline
                $this->saveTimeline($retweet);
            }
        }
        
        $this->end($result);
    }
    
    /**
     * Unretweet a tweet
     * @param int $tweet_id ID of a tweet
     */
    public function actionUnretweet(){
        // Get param
        $tweet_id = Yii::app()->request->getParam("tweet_id");
        $tweet_id = Tweets::findRealTweetID($tweet_id);
        $user_id  = Yii::app()->user->id;
        
        // Find Model
        $retweet  = Tweets::model()->findByAttributes(array(
            'user_id'   => $user_id,
            'parent_id' => $tweet_id,
            'content'   => NULL,
        ));
        
        // If haven't retweeted before, create a new one
        $result = array('result'=>1);
        if ($retweet){
            if (!$retweet->delete()){
                $result['result'] = 0;
                $result['errors'] = $retweet->errors;
            }
        }
        
        $this->end($result);
    }
    
    public function actionFavoritedBy(){
        // Get param
        $tweet_id = Yii::app()->request->getParam("tweet_id");
        $since_id = Yii::app()->request->getParam("since_id");
        $max_id   = Yii::app()->request->getParam("max_id");
        $count    = Yii::app()->request->getParam("count", 20);
        
        // Build query
        $criteria = new CDbCriteria();
        $criteria->addCondition("tweet_id = :tweet_id");
        $params = array(':tweet_id'  =>$tweet_id,);
        if ($since_id) {
            $criteria->addCondition('t.id < :since_id');
            $params[':since_id'] = $since_id;
        }
        if ($max_id) {
            $criteria->addCondition ('t.id > :max_id');
            $params[':max_id'] = $max_id;
        }
        $criteria->params = $params;
        $criteria->order  = 't.id DESC';
        $criteria->select = 't.id';
        $criteria->with   = array('user'=>array('select'=>'id , username, full_name, avatar'));
        $criteria->limit  = $count;
        
        $favorites = Favorites::model()->findAll($criteria);
        $result = array();
        foreach ($favorites as $favorite){
            $user = $favorite->user;
            $result[] = array(
                'user_id'  => $user->id,
                'username' => $user->username,
                'full_name'=> $user->full_name,
                'avatar'   => Html::avatarUrl($user->avatar),
                'status'   => $this->checkFollowStatus($user->id),
                'order_id' => $favorite->id,
            );
        }
        $this->end($result);
    }
    
    public function actionRetweetedBy(){
        // Get param
        $tweet_id = Yii::app()->request->getParam("tweet_id");
        $since_id = Yii::app()->request->getParam("since_id");
        $max_id   = Yii::app()->request->getParam("max_id");
        $count    = Yii::app()->request->getParam("count", 20);
        
        $criteria = new CDbCriteria();
        $criteria->with      = array('user');
        $criteria->select    = 't.id';
        $criteria->condition = "parent_id = :tweet_id";
        $criteria->params    = array(":tweet_id"=> $tweet_id);
        $criteria->addCondition("t.id <> :tweet_id");
        $criteria->addCondition("content is not null");
        if ($since_id) {
            $criteria->addCondition('t.id < :since_id');
            $params[':since_id'] = $since_id;
        }
        if ($max_id) {
            $criteria->addCondition ('t.id > :max_id');
            $params[':max_id'] = $max_id;
        }
        $criteria->order = "t.id DESC";
        $criteria->group = "user.id";
        $criteria->limit = $count;
        $tweets = Tweets::model()->findAll($criteria);
        
        $result = array();
        foreach ($tweets as $tweet){
            $user = $tweet->user;
            $result[] = array(
                'user_id'  => $user->id,
                'username' => $user->username,
                'full_name'=> $user->full_name,
                'avatar'   => Html::avatarUrl($user->avatar),
                'status'   => $this->checkFollowStatus($user->id),
                'follows_id' => $tweet->id,
            );
        }
        $this->end($result);
    }
    
    private function checkFollowStatus($user_id) {
        $follow = Follows::model()->findByAttributes(array(
            'user_id'   => Yii::app()->user->id,
            'follow_id' => $user_id,
        ),
                array('select'=>'id'));
        return ($follow)?"1":null;
    }
    
    public function actionListReplyParent() {
        // Get param
        $tweet_id = Yii::app()->request->getParam('tweet_id');
        echo "[";
        $tweet    = Tweets::model()->findByPk($tweet_id);
        if ($tweet && ($tweet->content) && ($tweet->parent != $tweet->id))
            $this->getParentInfo($tweet->parent);
        echo "]";
    }
    
    private function getParentInfo($tweet){
        if (($tweet->parent_id != $tweet->id) &&($tweet->content != "")){
            $this->getParentInfo($tweet->parent);
            echo ",";
        }
        echo CJSON::encode($this->getTweetInfo($tweet));
    }
    
    public function actionListReply() {
        // Get param
        $user_id  = Yii::app()->user->id;
        $count    = Yii::app()->request->getParam("count", 20);
        $since_id = Yii::app()->request->getParam("since_id");
        $max_id   = Yii::app()->request->getParam("max_id");
        $tweet_id = Yii::app()->request->getParam("tweet_id");
        
        $replies = Tweets::model()->findAll("id <> '$tweet_id' and parent_id = '$tweet_id' and content is not null");
        //echo "[";
        $result = array();
        if ($replies)
            foreach ($replies as $reply){
                $this->getChildInfo($reply, $result);
            }
        //echo "]";
        $this->end($result);
    }
    
    
    /**
     * Print info of tweet and its children
     * @param Tweet $tweet
     */
    private function getChildInfo($tweet, &$result ){
        $result[] = ($this->getTweetInfo($tweet));
        $replies = Tweets::model()->findAll("id <> '$tweet->id' and parent_id = '$tweet->id' and content is not null");
        foreach ($replies as $reply){
            $this->getChildInfo($reply, $result);
        }
    }
    
    
    /**
     * Get user timeline of desired user
     */
    public function actionUser_timeline(){
        $tweetInfos = TweetCore::user_timeline(Yii::app()->request->getParam('user_id', Yii::app()->user->id));
        $this->end($tweetInfos);
    }
    
    /**
     * Get home timeline of desired user
     */
    public function actionHome_timeline2(){
        // Get param
        $user_id  = Yii::app()->request->getParam("user_id", Yii::app()->user->id);
        $count    = Yii::app()->request->getParam("count", 20);
        $since_id = Yii::app()->request->getParam("since_id");
        $max_id   = Yii::app()->request->getParam("max_id");
        
        // Build query
        $criteria = new CDbCriteria();
        // Basic param
        $criteria->limit = $count;
        $criteria->order = "t.tweet_id DESC";
        $criteria->addCondition("t.user_id = :user_id");
        $params = array(':user_id'  =>$user_id,);
        if ($since_id) {
            $criteria->addCondition('t.tweet_id < :since_id');
            $params[':since_id'] = $since_id;
        }
        if ($max_id) {
            $criteria->addCondition ('t.tweet_id > :max_id');
            $params[':max_id'] = $max_id;
        }
        $criteria->params = $params;
        
        
        $criteria->select = array(
            '*',
            'IF (tweet.content is null, tweet.parent_id, tweet.id) as real_tweet_id',
        );
        $criteria->with  = array(
            'tweet'
        );
        $criteria->group = 'real_tweet_id';
        
        // Get result
        $timelines = Timeline::model()->findAll($criteria);
        
        /*@var $tweet Tweets */
        $tweetInfos = array();
        foreach ($timelines as $timeline){
            $tweet = $timeline->tweet;
            $tweetInfos[] = $this->getTweetInfo($tweet);
        }
        
        $this->end($tweetInfos);
    }
    
    /**
     * Get home timeline of desired user
     */
    public function actionHome_timeline(){
        $tweetInfos = TweetCore::home_timeline();
        
        $this->end($tweetInfos);
    }
    
    /**
     * Get info of a original tweet
     * @param int $tweet_id ID of a tweet
     * @return JSON {'tweet', 'replys'}
     */
    public function actionTweetInfo()
    {
        // Get param
        $tweet_id = Yii::app()->request->getParam('tweet_id');
        
        $tweet = Tweets::model()->findByPk($tweet_id);
        if (!$tweet) return;
        
        $result = array();
        $result['tweet'] = $this->getTweetInfo($tweet);
        
        $this->end($result);
    }
    
    public function actionTweetExpand(){
        $count    = 7;
        $tweet_id = Yii::app()->request->getParam('tweet_id');
        
        $countFav = Tweets::countFavorites($tweet_id);
        $countRet = Tweets::countRetweets($tweet_id);
        
        // Get users who retweeted this tweet
        $criteria = new CDbCriteria();
        $criteria->with      = 'user';
        $criteria->condition = "parent_id = :tweet_id";
        $criteria->params    = array(":tweet_id"=> $tweet_id);
        $criteria->addCondition("t.id <> :tweet_id");
        $criteria->addCondition("content is null");
        $criteria->limit = $count;
        $tweets = Tweets::model()->findAll($criteria);
        
        $users = array();
        foreach ($tweets as $tweet){
            $user = $tweet->user;
            $users[] = $this->getUserInfo($user);
        }
        $count -= sizeof($users);
        if ($count > 0){
            // Get users who favorited this tweet
            $criteria = new CDbCriteria();
            $criteria->limit = $count;
            $criteria->condition = '';
            $favorites = Favorites::model()->findAll(array(
                'limit'=>$count,
                'condition'=> 'tweet_id = :tweet_id',
                'params'=> array(':tweet_id'=>$tweet_id),
            ));
            foreach ($favorites as $favorite){
                $user = $favorite->user;
                $users[] = $this->getUserInfo($user);
            }
        }
        $result = array(
            'countFav'=>$countFav,
            'countRet'=>$countRet,
            'users'   =>$users,
        );
        $this->end($result);
        
    }
    
    /**
     * 
     * @param Users $user
     */
    private function getUserInfo($user){
        return array(
            'user_id'   => $user->id,
            'avatar'    => Html::avatarUrl($user->avatar),
            'username'  => $user->username,
            'full_name' => $user->full_name,
        );
    }
    
    public function actionCountRefav()
    {
        // Get param
        $tweet_id = Yii::app()->request->getParam('tweet_id');
        
        $result = array();
        $result['count_favorites'] = Tweets::model()->countFavorites($tweet_id);
        $result['count_retweets'] = Tweets::model()->countRetweets($tweet_id);
        
        $this->end($result);
    }
    
    
    /**
     * Get a tweet info
     * @param Tweets $tweet
     * @return array info
     */
    public function getTweetInfo($tweet){
        $retweeted_by = "";
        if (!$tweet->content){
            if ($tweet->user->id != Yii::app()->user->id)
                $retweeted_by = $tweet->user->full_name;
            $tweet = $tweet->parent;
        }
        return array(
            'tweet_id' => $tweet->id,
            'content'  => $tweet->content,
            'parent_id'=> $tweet->parent_id,
            'created_time' => $tweet->created_time,
            'user_id'      => $tweet->user_id,
            'username'     => $tweet->user->username,
            'full_name'    => $tweet->user->full_name,
            'avatar'       => Html::avatarUrl($tweet->user->avatar),
            'retweeted_by'    => $retweeted_by,
            'refav'        => "{$this->calcRefav($tweet)}",
        );
    }
    
    public function calcRefav($tweet){
        $refav = 0;
        if (Tweets::model()->findByAttributes(
                array(
                    'parent_id'=>$tweet->id,
                    'user_id'  =>Yii::app()->user->id, 
                    'content'  =>null,
                ),
                array(
                    'select'=>'id',
                )
        )) $refav++;
        if (Favorites::model()->findByAttributes(
                array('user_id'=>Yii::app()->user->id, 'tweet_id'=>$tweet->id),
                array(
                    'select'=>'id',
                )
        )) $refav+=10;
        return $refav;
                
    }
    
    /**
     * actionDiscovery
     */
    public function actionDiscovery() {
    	$id = 'discovery';
    	$discovery = Yii::app()->cache->get($id);
        $discovery = FALSE;
    	if ($discovery === false) {
    		$discovery = Yii::app()->db->createCommand()
    		//->select('t.id AS tweet_id, t.content, t.user_id, u.username, u.full_name, CONCAT(:path,u.avatar) AS avatar, t.created_time, COUNT(t.id) AS ridx, f.fidx')
    		->select('t.id AS tweet_id, COUNT(t.id) AS ridx, f.fidx')
                ->from('tweets AS t')
    		//->leftJoin('users AS u', 'u.id = t.user_id')
    		->leftJoin('(SELECT tweet_id, COUNT(id) AS fidx FROM favorites GROUP BY tweet_id) AS f', 't.id = f.tweet_id')
    		->group('t.parent_id')
    		//->order('ridx DESC, fidx DESC, t.id DESC')
                ->order('ridx DESC, fidx DESC')
    		->limit(20)
    		->queryAll(true, array('path'=>Yii::app()->createAbsoluteUrl('avatars').'/'));
    			
    		Yii::app()->cache->set($id, $discovery, 3600);
    	}
        
        $result = array();
        foreach ($discovery as $dis){
            $tweet = Tweets::model()->findByPk($dis['tweet_id']);
            if ($tweet)
                $result[] = $this->getTweetInfo($tweet);
        }
    	$this->end($result);
    }
    
    
    public function filters() {
        return array(
            'accessControl',
        );
    }

    public function accessRules() {
        return array(
            array('allow', // allow authenticated users to access all actions　　（　されたユーザーはすべてのアクションへのアクセスを許可する　）
                'users' => array('@'),
            ),
            array('deny', // deny all users　(すべてのユーザーを拒否する。)
                'users' => array('*'),
            ),
        );
    }
}
